local HG = {}

local H = require "packages.ol_hegemony.util"
local U = require "packages.utility.utility"


--- 交换主副将
---@param room Room
---@param player ServerPlayer
---@return boolean
function HG.SwapMainAndDeputy(room, player)
  local general1 = player.general
  local general2 = player.deputyGeneral
  if not (general1 and general2) then return false end
  if general1 == "anjiang" then player:revealGeneral(false, true) end
  if general2 == "anjiang" then player:revealGeneral(true, true) end
  general1 = player.general
  general2 = player.deputyGeneral
  if string.find(general1, "lord")
   or string.find(general1, "zhonghui") or string.find(general1, "simazhao")
   or string.find(general1, "sunchen") or string.find(general1, "gongsunyuan")
  then return false end
  -- local tolose = {}
  --先把技能lose掉，就不会触发handleaddlose的失去技能效果了(不丢武将牌上的牌)
  for _, s in ipairs(Fk.generals[general1]:getSkillNameList()) do
    if not Fk.skills[s]:hasTag(Skill.MainPlace) then
      if player:hasSkill(s, true, true) then
        local lost_skills = player:loseSkill(s)
        for _, ss in ipairs(lost_skills) do
          room:doBroadcastNotify("LoseSkill", {
            player.id,
            ss.name
          })
        end
      -- else
      --   table.insert(tolose, s)
      end
    end
  end
  for _, s in ipairs(Fk.generals[general2]:getSkillNameList()) do
    if not Fk.skills[s]:hasTag(Skill.DeputyPlace) then
      if player:hasSkill(s, true, true) then
        local lost_skills = player:loseSkill(s)
        for _, ss in ipairs(lost_skills) do
          room:doBroadcastNotify("LoseSkill", {
            player.id,
            ss.name
          })
        end
      -- else
      --   table.insert(tolose, s)
      end
    end
  end
  room:changeHero(player, "blank_shibing", false, true, false, false, false)
  room:changeHero(player, general2, false, false, true, false, false)
  room:changeHero(player, general1, false, true, true, false, false)
  room:doSuperLightBox("packages/ol_hegemony/qml/SwapTwoPlayersDeputy.qml",{
    generalLeft = general1,
    generalRight = general2,
  })
  -- if table.contains(player:getTableMark("@hs__duanchang"), general1) then -- 耦合断肠
  --   table.insertTableIfNeed(tolose, Fk.generals[general1]:getSkillNameList())
  -- end
  -- if table.contains(player:getTableMark("@hs__duanchang"), general2) then -- 耦合断肠
  --   table.insertTableIfNeed(tolose, Fk.generals[general2]:getSkillNameList())
  -- end
  -- if #tolose > 0 then
  --   room:handleAddLoseSkills(player, "-"..table.concat(tolose, "|-"), nil, false)
  -- end
  return true
end

--- 推举
---@param room Room
---@param player ServerPlayer
---@param skillName string
---@return ServerPlayer?
function HG.DoElectedChange(room, player, skillName)
  local kingdom = player:getMark("__heg_kingdom")
  if kingdom == "wild" then
    kingdom = player:getMark("__heg_init_kingdom")
  end
  local generals = room:findGenerals(function(g)
    return Fk.generals[g].kingdom == kingdom or Fk.generals[g].subkingdom == kingdom
  end, 1)
  local general = room:askToChooseGeneral(player, {generals = generals, n = 1, no_convert = true}) ---@type string
  room:sendLog{
    type = "#ElectedChangeLog",
    from = player.id,
    arg = general,
    arg2 = skillName,
    toast = true,
  }
  local targets = table.filter(room.alive_players, function(p) return H.compareKingdomWith(p, player) end)
  room:sortByAction(targets)
  local ret
  for _, p in ipairs(targets) do
    local choices = {"Cancel"}
    if p.general ~= "anjiang" and not p.general:startsWith("ld__lord") then
      table.insert(choices, 1, "#elected_change_main:::" .. general)
    end
    if p.deputyGeneral ~= "anjiang" then
      table.insert(choices, 2, "#elected_change_deputy:::" .. general)
    end
    local choice = room:askToChoice(p, {
      choices = choices,
      skill_name = "ElectedChange",
      prompt = "#elected_change-ask:" .. player.id .. "::" .. general})
    if choice ~= "Cancel" then
      if choice:startsWith("#elected_change_main") then
        generals = {H.getActualGeneral(p, false)}
        room:changeHero(p, general, false, false, true, false, false)
        room.logic:trigger(H.TransformGeneraled, p, general)
      else
        generals = {H.getActualGeneral(p, true)}
        room:changeHero(p, general, false, true, true, false, false)
        room.logic:trigger(H.TransformGeneraled, p, general)
      end
      ret = p
      break
    end
  end
  room:returnToGeneralPile(generals)
  return {ret, general}
end

Fk:loadTranslationTable{
  ["ElectedChange"] = "推举",
  [":ElectedChange"] = "<b>推举</b>：推举角色展示一张与其势力相同的武将牌，每名与其势力相同的角色选择是否将此武将牌替换其已明置的主将或副将。" ..
  "若有角色选择替换，称为该角色<u>选用</u>，停止对后续角色的询问，结束推举流程。",
  ["#Elected"] = "推举了",
  ["#ElectedChangeLog"] = "%from 由于 “%arg2”，推举了 %arg",
  ["#elected_change_main"] = "选用：将%arg作为主将",
  ["#elected_change_deputy"] = "选用：将%arg作为副将",
  ["#elected_change-ask"] = "%src 推举了 %arg，你可 选用 为你的主将或副将",
}

--- 遣调 （需要修复和变更的兼容）
---@param room Room
---@param player ServerPlayer
---@param target ServerPlayer
---@param deputyName string
---@param kingdom string
---@param isDeputy boolean
function HG.DoGiveDeputy(room, player, target, deputyName, kingdom, isDeputy)
  local orig_before = isDeputy and (player.deputyGeneral or "") or player.general
  local orig_befores = Fk.generals[orig_before]
  local orig_skills_before = orig_befores and orig_befores:getSkillNameList() or {}
  -- H.removeGeneral(room, player, isDeputy)
  player:revealGeneral(isDeputy, true) -- 先摆
  local orig = isDeputy and (player.deputyGeneral or "") or player.general
  local data = {
    who = player,
    isDeputy = isDeputy,
    origName = orig,
  }
  player:setMark("CompanionEffect", 0)
  player:setMark("HalfMaxHpLeft", 0)
  player:doNotify("SetPlayerMark", { player.id, "CompanionEffect", 0})
  player:doNotify("SetPlayerMark", { player.id, "HalfMaxHpLeft", 0})
  if room.logic:trigger(H.GeneralRemoving, player, data) then return false end
  orig = isDeputy and (player.deputyGeneral or "") or player.general
  if orig:startsWith("blank_") then return false end
  local origs = Fk.generals[orig]
  local orig_skills = origs and origs:getSkillNameList() or {}
  local new_general = origs.gender == General.Male and "blank_shibing" or "blank_nvshibing"
  orig_skills = table.map(orig_skills, function(e)
    return "-" .. e
  end)
  room:handleAddLoseSkills(player, table.concat(orig_skills, "|"), nil, false)
  if isDeputy then
    room:setPlayerProperty(player, "deputyGeneral", new_general)
    room:setPlayerMark(player, "__heg_deputy", new_general)
  else
    room:setPlayerProperty(player, "general", new_general)
    room:setPlayerMark(player, "__heg_general", new_general)
  end
  player:filterHandcards()
  room:sendLog{
    type = "#GeneralRemoved",
    from = player.id,
    arg = isDeputy and "deputyGeneral" or "mainGeneral",
    arg2 = origs.name,
  }
  -- room:returnToGeneralPile({orig.name})
  room.logic:trigger(H.GeneralRemoved, player, data)

  local getskills = table.filter(orig_skills_before, function (s) 
    return (not Fk.skills[s]:hasTag(Skill.MainPlace)) and (not Fk.skills[s]:hasTag(Skill.DeputyPlace))
  end)
  room:handleAddLoseSkills(target, table.concat(getskills, "|"), nil, false)
  room:setPlayerMark(target, "@wk_give_deputy", deputyName)
  room:setPlayerMark(target, "@wk_give_deputy_kingdom", kingdom)
end

--- 结束遣调
---@param room Room
---@param player ServerPlayer
---@param skillName string
function HG.StopGiveDeputy(room, player, skillName)
  local orig_string = player:getMark("@wk_give_deputy")
  local orig = Fk.generals[orig_string]
  local giveback = false
  local orig_skills = orig and orig:getSkillNameList() or {}
  orig_skills = table.map(orig_skills, function(e)
    return "-" .. e
  end)
  room:handleAddLoseSkills(player, table.concat(orig_skills, "|"), nil, false)

  local targets = table.filter(room.alive_players, function(p)
    return p.kingdom == player:getMark("@wk_give_deputy_kingdom")
    and (p.deputyGeneral:startsWith("blank_") or p.general:startsWith("blank_")) end)
  if #targets > 0 then
    local to = room:askToChoosePlayers(player,{
      targets = targets,
      min_num = 1,
      max_num = 1,
      prompt = "#wk_give_deputy-choose",
      skill_name = skillName,
      cancelable = false,
    })
    if #to > 0 then
      local target = to[1]
      local choices = {}
      if target.deputyGeneral:startsWith("blank_") then
        table.insert(choices, "wk_give_deputy_back")
      end
      if target.general:startsWith("blank_") then
        table.insert(choices, "wk_give_main_back")
      end
      if #choices > 0 then
        local choice = room:askToChoice(player, {choices = choices,  skill_name = skillName})
        if choice == "wk_give_deputy_back" then
          room:changeHero(target, orig_string, false, true, true, false, false)
          room.logic:trigger(H.TransformGeneraled, target, orig_string)
        else
          room:changeHero(target, orig_string, false, false, true, false, false)
          room.logic:trigger(H.TransformGeneraled, target, orig_string)
        end
        giveback = true
      end
    end
  end
  if not giveback then
    room:returnToGeneralPile({orig_string})
  end
  room:setPlayerMark(player, "@wk_give_deputy", 0)
  room:setPlayerMark(player, "@wk_give_deputy_kingdom", 0)
end

Fk:loadTranslationTable{
  ["@wk_give_deputy"] = "遣调",
  ["@wk_give_deputy_kingdom"] = "属国",

  [":DoGiveDeputy"] = "<b><font color = 'orange'>遣调：遣调角色执行遣调流程时，移除对应的武将牌并将之置于目标角色的遣调区内，视为该角色拥有此武将牌，遣调区内的武将牌不为主副将且不能被移除、暗置和变更。"..
  "遣调结束后，目标角色选择遣调武将牌原本所属国家所有角色的一张士兵牌，令此士兵牌变更为遣调武将牌。</font>",

  ["#wk_give_deputy-choose"] = "遣调：选择该遣调武将牌原所属国家一名有士兵牌的角色，结束遣调并返还此武将牌于其",
  ["wk_give_deputy_back"] = "返还至 副将",
  ["wk_give_main_back"] = "返还至 主将",
}

--- 获得战策
---@param room Room
---@param player ServerPlayer
---@param num integer
---@param skillName string
function HG.GetPolicy(room, player, num, skillName)
  local all_policies = { "drum_together", "backarm_stop", "other_replace", "reverse_attack", "trueaim_another", "buy_arm",
          "move_insecret", "getlive_indeath", "attack_infire", "replace_something", "move_people", "getchance_insmall"}
  for _, p in ipairs(room.alive_players) do
    for _, s in ipairs(p.player_skills) do
      if table.contains(all_policies, s.name) then
        table.removeOne(all_policies, s.name)
      end
    end
  end
  local count = num
  if #all_policies < num then count = #all_policies end
  if count == 0 then return end
  local policies = table.random(all_policies, count)
  for i = 1, #policies, 1 do
    room:handleAddLoseSkills(player, policies[i], nil, false)
    U.setPrivateMark(player, policies[i].."_mark", { "@["..policies[i].."_mark]" })
  end
  room:doBroadcastNotify("ShowToast", Fk:translate(player.general) .. '/' .. Fk:translate(player.deputyGeneral) .. " " .. Fk:translate("#PolicyGet") .. " " 
  .. U.ConvertNumber(num) .. " " ..Fk:translate("#PolicyCard"))
  room:sendLog{
    type = "#GetPolicyLog",
    from = player.id,
    arg = count,
    arg2 = skillName,
  }
end

--- 获得某角色战策数量
---@param player ServerPlayer
function HG.GetPolicyNum(player)
  local all_policies = { "drum_together", "backarm_stop", "other_replace", "reverse_attack", "trueaim_another", "buy_arm",
          "move_insecret", "getlive_indeath", "attack_infire", "replace_something", "move_people", "getchance_insmall"}
  local policies = {}
  for _, s in ipairs(player.player_skills) do
    if table.contains(all_policies, s.name) then
      table.insertIfNeed(policies, s.name)
    end
  end
  return #policies
end

Fk:loadTranslationTable{
  ["#PolicyGet"] = "获得了",
  ["#PolicyCard"] = "战策",

  ["@[private]drum_together_mark"] = "战策",
  ["@[private]backarm_stop_mark"] = "战策",
  ["@[private]other_replace_mark"] = "战策",
  ["@[private]reverse_attack_mark"] = "战策",
  ["@[private]trueaim_another_mark"] = "战策",
  ["@[private]buy_arm_mark"] = "战策",
  ["@[private]move_insecret_mark"] = "战策",
  ["@[private]getlive_indeath_mark"] = "战策",
  ["@[private]attack_infire_mark"] = "战策",
  ["@[private]replace_something_mark"] = "战策",
  ["@[private]move_people_mark"] = "战策",
  ["@[private]getchance_insmall_mark"] = "战策",

  ["#GetPolicyLog"] = "%from 由于 “%arg2”，获得了 %arg 张“战策”。",

  ["unknownpolicy"] = "战策",
  [":unknownpolicy"] = "未知<a href=':Heg__policy'>战策</a>",

  ["drum_together"] = "金鼓齐鸣",
  ["backarm_stop"] = "偃甲息兵",
  ["other_replace"] = "李代桃僵",
  ["reverse_attack"] = "反守为攻",
  ["trueaim_another"] = "声东击西",
  ["buy_arm"] = "招兵买马",
  ["move_insecret"] = "暗度陈仓",
  ["getlive_indeath"] = "绝处逢生",
  ["attack_infire"] = "趁火打劫",
  ["replace_something"] = "偷梁换柱",
  ["move_people"] = "调兵遣将",
  ["getchance_insmall"] = "见缝插针",

  [":drum_together"] = "当你造成伤害后，与你势力相同的角色各摸一张牌。",
  [":backarm_stop"] = "其他角色使用牌指定你为目标后，你与其依次收回装备区内所有牌且本回合不能装备牌。",
  [":other_replace"] = "其他角色受到不小于其体力值的伤害时，防止之，然后你摸一张牌且伤害来源对你造成等量伤害。",
  [":reverse_attack"] = "当你响应其他角色使用的牌结算后，视为对其使用一张 【杀】。",
  [":trueaim_another"] = "当你使用牌选择唯一目标后，改为指定一名其他角色。",
  [":buy_arm"] = "准备阶段，弃置一张手牌，检索并获得一张装备牌。",
  [":move_insecret"] = "结束阶段，对一名本回合使用过【闪】或【无懈可击】的角色造成1点伤害。",
  [":getlive_indeath"] = "进入濒死状态时，弃置所有手牌，回复1点体力。",
  [":attack_infire"] = "其他角色进入濒死状态时，获得其区域内各一张牌。",
  [":replace_something"] = "距离1以内其他角色的出牌阶段开始时，观看其手牌并用一张手牌交换其中一张。",
  [":move_people"] = "结束阶段，令一名与你势力相同的角色与你交换副将。",
  [":getchance_insmall"] = "其他角色使用牌指定唯一目标时，弃置一张牌，成为此牌的额外目标。",

  [":Heg__policy"] = "<b><font color = 'orange'>战策：部分角色可以获得“战策”，获得“战策”时，其随机从“战策”库中抽取等量的“战策”并视为拥有此“战策”对应的效果；"..
  "拥有“战策”的角色可以在对应的时机发动“战策”上的效果，然后将此“战策”洗回“战策”堆。</font><br></b>"..
  "1.金鼓齐鸣：当你造成伤害后，与你势力相同的角色各摸一张牌。<br>"..
  "2.偃甲息兵：其他角色使用牌指定你为目标后，你与其依次收回装备区内所有牌且本回合不能装备牌。<br>"..
  "3.李代桃僵：其他角色受到不小于其体力值的伤害时，防止之，然后你摸一张牌且伤害来源对你造成等量伤害。<br>"..
  "4.反守为攻：当你响应其他角色使用的牌结算后，视为对其使用一张 【杀】。<br>"..
  "5.声东击西：当你使用牌选择唯一目标后，改为指定一名其他角色。<br>"..
  "6.招兵买马：准备阶段，弃置一张手牌，检索并获得一张装备牌。<br>"..
  "7. 暗度陈仓：结束阶段，对一名本回合使用过【闪】或【无懈可击】的角色造成1点伤害。<br>"..
  "8.绝处逢生：进入濒死状态时，弃置所有手牌，回复1点体力。<br>"..
  "9.趁火打劫：其他角色进入濒死状态时，获得其区域内各一张牌。<br>"..
  "10.偷梁换柱：距离1以内其他角色的出牌阶段开始时，观看其手牌并用一张手牌交换其中一张。<br>"..
  "11.调兵遣将：结束阶段，令一名与你势力相同的角色与你交换副将。<br>"..
  "12.见缝插针：其他角色使用牌指定唯一目标时，弃置一张牌，成为此牌的额外目标。",
}

local NilEvent = TriggerEvent:subclass("NilEvent")
--- 失去战策后
---@class HG.HegLosePolicy: NilEvent
HG.HegLosePolicy = NilEvent:subclass("HG.HegLosePolicy")

--- 获得某对象标记
---@param object Card|Player
---@param markname string
---@param suffixes string[]
---@return boolean
function HG.HasMark(object, markname, suffixes)
  if not object then return false end
  for mark, _ in pairs(object.mark) do
    if mark == markname then return true end
    if mark:startsWith(markname .. "-") then
      for _, suffix in ipairs(suffixes) do
        if mark:find(suffix, 1, true) then return true end
      end
    end
  end
  return false
end

---------------------------------------------------------------------------------------------------
--明置牌（将一些手牌对所有角色可见。官方仅国际服国战田豫有此功能）

--贴有此标记的牌在手牌中明置，离开手牌时清除。可带"-phase"、"-turn"等suffix
MarkEnum.ShownCards = "@@ShownCards-inhand"

--明置手牌
---@param player ServerPlayer @ 目标角色
---@param card integer | integer[] | Card | Card[] @ 要明置的牌
---@param scope? integer @ 明置的历史范围，默认永久
HG.showCards = function (player, card, scope)
  player.room:addSkill("#ShownCards")
  local suffix_mapper = {
    [Player.HistoryGame] = "",
    [Player.HistoryRound] = "-round",
    [Player.HistoryTurn] = "-turn",
    [Player.HistoryPhase] = "-phase",
  }
  scope = scope or Player.HistoryGame
  local ids = Card:getIdList(card)
  ids = table.filter(ids, function (id)
    return table.contains(player:getCardIds("h"), id)
  end)
  if #ids > 0 then
    for _, id in ipairs(ids) do
      player.room:setCardMark(Fk:getCardById(id), MarkEnum.ShownCards..suffix_mapper[scope], 1)
    end
    player:showCards(ids)
  end
end

--暗置手牌
---@param player ServerPlayer @ 目标角色
---@param card integer | integer[] | Card | Card[] @ 要暗置的牌
HG.hideCards = function (player, card)
  local room = player.room
  local ids = Card:getIdList(card)
  ids = table.filter(ids, function (id)
    return table.contains(player:getCardIds("h"), id)
  end)
  if #ids > 0 then
    for _, id in ipairs(ids) do
      room:setCardMark(Fk:getCardById(id), MarkEnum.ShownCards, 0)
      for _, suffix in ipairs(MarkEnum.TempMarkSuffix) do
        room:setCardMark(Fk:getCardById(id), MarkEnum.ShownCards..suffix, 0)
      end
    end
  end
end

--获得一名角色的明置手牌id列表
---@param player ServerPlayer | Player @ 目标角色
---@return integer[]
HG.getShownCards = function (player)
  return table.filter(player:getCardIds("h"), function (id)
    if Fk:getCardById(id):getMark(MarkEnum.ShownCards) > 0 then
      return true
    end
    if table.find(MarkEnum.TempMarkSuffix, function (suffix)
      return Fk:getCardById(id):getMark(MarkEnum.ShownCards..suffix) > 0
    end) then
      return true
    end
    return false
  end)
end

--获得一名角色的暗置手牌id列表
---@param player ServerPlayer | Player @ 目标角色
---@return integer[]
HG.getHiddenCards = function (player)
  return table.filter(player:getCardIds("h"), function (id)
    if Fk:getCardById(id):getMark(MarkEnum.ShownCards) == 0 and table.every(MarkEnum.TempMarkSuffix, function (suffix)
      return Fk:getCardById(id):getMark(MarkEnum.ShownCards..suffix) == 0
    end) then
      return true
    end
    return false
  end)
end

---@class AskToCardsParams: AskToUseActiveSkillParams
---@field skip? boolean @ 是否跳过明置，默认不跳过

--- 询问一名玩家明置若干张手牌，返回选择的牌。与本体askToCards类似。
--- 与本体askToCards类似
---@param player ServerPlayer @ 要询问的玩家
---@param params AskToCardsParams @ 各种变量
---@return integer[] @ 选择的牌的id列表，可能是空的
HG.askToShowCards = function (player, params)
  local room = player.room
  local cancelable = params.cancelable or params.cancelable == nil
  local cards = table.filter(player:getCardIds("h"), function (id)
    return not table.contains(HG.getShownCards(player), id)
  end)
  if #cards == 0 then return {} end
  local ret = room:askToCards(player, {
    min_num = params.min_num,
    max_num = params.max_num,
    include_equip = false,
    skill_name = params.skill_name,
    pattern = tostring(Exppattern{ id = cards }),
    prompt = params.prompt,
    cancelable = cancelable,
  })
  if not cancelable and #ret == 0 then
    ret = table.random(cards, params.min_num)
  end
  if #ret > 0 and (params.skip or params.skip == nil) then
    HG.showCards(player, ret)
  end
  return ret
end

--- 描述和武将牌回到武将牌堆有关的数据
---@class ReturnToGeneralPileDataSpec
---@field generals string[] @ 武将名数组
---@field position string? @ 位置，top/bottom/random，默认random

--- 武将牌回到武将牌堆事件数据
---@class ReturnToGeneralPileData: ReturnToGeneralPileDataSpec, TriggerData
ReturnToGeneralPileData = TriggerData:subclass("ReturnToGeneralPileData")

--- 武将牌回到武将牌堆事件
---@class ReturnToGeneralPileEvent: TriggerEvent
---@field data ReturnToGeneralPileData
HG.ReturnToGeneralPileEvent = TriggerEvent:subclass("ReturnToGeneralPileEvent")

---@class AfterReturnToGeneralPile: ReturnToGeneralPileEvent
HG.AfterReturnToGeneralPile = HG.ReturnToGeneralPileEvent:subclass("AfterReturnToGeneralPile")

---@alias ReturnToGeneralPileFunc fun(self: TriggerSkill, event: ReturnToGeneralPileEvent,
---  target: nil, player: ServerPlayer, data: ReturnToGeneralPileData):any

---@class SkillSkeleton
---@field public addEffect fun(self: SkillSkeleton, key: ReturnToGeneralPileEvent,
---  data: TrigSkelSpec<ReturnToGeneralPileFunc>, attr: TrigSkelAttribute?): SkillSkeleton

--- 把武将牌塞回去（……）
---@param self Room @ 房间
---@param g string[] @ 武将名数组
---@param position string? @位置，top/bottom/random，默认random
---@return boolean @ 是否成功
function HG.returnToGeneralPile(self, g, position)
  position = position or "random"
  local data= {
    generals = table.simpleClone(g),
    position = position,
  }
  assert(position == "top" or position == "bottom" or position == "random")
  if position == "bottom" then
    table.insertTable(self.general_pile, g)
  elseif position == "top" then
    while #g > 0 do
      table.insert(self.general_pile, 1, table.remove(g))
    end
  elseif position == "random" then
    while #g > 0 do
      table.insert(self.general_pile, math.random(math.max(#self.general_pile - 1, 1)),
                   table.remove(g))
    end
  end
  self.logic:trigger(HG.AfterReturnToGeneralPile, nil, data)
  return true
end



return HG